/*
 * logger.cpp
 *
 *  Created on: 17.01.2012
 *      Author: knz
 */

#include "logger.h"

#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <unistd.h>

#include <syslog.h>
#include <iostream>
#include <cstdarg>
#include <cerrno>

Logger *g_pLog = 0;

Logger* Logger::createLogger(const char *pchLogName, bool bSyslog)
{
    if (g_pLog == 0) g_pLog = new Logger(pchLogName, bSyslog);
    return g_pLog;
}

Logger::Logger(const char *pchLogName, bool bSyslog)
{
    if (pchLogName == 0) {
        if (bSyslog) {
            m_pchLogName = new char[20];
            strcpy(m_pchLogName, "json-test-server");
        } else {
            m_pchLogName = new char[20];
            strcpy(m_pchLogName, "json-test-server.log");
        }
    } else {
        m_pchLogName = new char[strlen(pchLogName)+1];
        strcpy(m_pchLogName, pchLogName);
    }

    m_bSyslog = bSyslog;
    if (m_bSyslog) {
        openlog(pchLogName, LOG_PID | LOG_CONS, LOG_USER);
    } else {

    }

    // Выставим уровень журналирования на все сообщения, кроме уровня TL_DEBUG
    m_iTrimLevel = LOG_INFO;
    // Выключим перенаправление логов на консоль
    m_bToConsole = false;

    m_iBufSize = 128;
    m_pchBuf = new char[m_iBufSize];
}

Logger::~Logger()
{
   g_pLog = 0;
   if (m_bSyslog) {
       closelog();
   } else {
       delete m_pchLogName;
   }
   if (m_pchBuf) delete [] m_pchBuf;
   if (m_pchLogName) delete [] m_pchLogName;
}

/*!
  Сообщения выше установленного уровня отбрасываются

  \sa Константы LOG_xxx в файле syslog.h
  */
void Logger::setTrimLevel(int iTrimLevel)
{
        m_iTrimLevel = iTrimLevel;
}

void Logger::setConsoleSwitch(bool bToConsole)
{
    m_bToConsole = bToConsole;
}

void Logger::addEmerg(const char *msg, ...)
{
    va_list(ap);
    va_start(ap, msg);

    if (m_bToConsole) {
        vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
        saveToStdErr("Emerg");
    } else {
        if (m_bSyslog) {
            vsyslog(LOG_EMERG, msg, ap);
        } else {
            vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
            saveToFile("Emerg");
        }
    }
    va_end(ap);
}

void Logger::addAlert(const char *msg, ...)
{
    if (m_iTrimLevel < LOG_ALERT) return;

    va_list(ap);
    va_start(ap, msg);

    if (m_bToConsole) {
        vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
        saveToStdErr("Alert");
    } else {
        if (m_bSyslog) {
            vsyslog(LOG_ALERT, msg, ap);
        } else {
            vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
            saveToFile("Alert");
        }
    }
    va_end(ap);
}

void Logger::addCritical(const char *msg, ...)
{
    if (m_iTrimLevel < LOG_CRIT) return;

    va_list(ap);
    va_start(ap, msg);

    if (m_bToConsole) {
        vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
        saveToStdErr("Crit");
    } else {
        if (m_bSyslog) {
            vsyslog(LOG_CRIT, msg, ap);
        } else {
            vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
            saveToFile("Crit");
        }
    }
    va_end(ap);
}

void Logger::addError(const char *msg, ...)
{
    if (m_iTrimLevel < LOG_ERR) return;

    va_list(ap);
    va_start(ap, msg);

    if (m_bToConsole) {
        vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
        saveToStdErr("Err");
    } else {
        if (m_bSyslog) {
            vsyslog(LOG_ERR, msg, ap);
        } else {
            vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
            saveToFile("Err");
        }
    }
    va_end(ap);
}

void Logger::addWarning(const char *msg, ...)
{
    if (m_iTrimLevel < LOG_WARNING) return;

    va_list(ap);
    va_start(ap, msg);

    if (m_bToConsole) {
        vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
        saveToStdErr("Warning");
    } else {
        if (m_bSyslog) {
            vsyslog(LOG_WARNING, msg, ap);
        } else {
            vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
            saveToFile("Warning");
        }
    }
    va_end(ap);
}

void Logger::addNotice(const char *msg, ...)
{
    if (m_iTrimLevel < LOG_NOTICE) return;

    va_list(ap);
    va_start(ap, msg);

    if (m_bToConsole) {
        vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
        saveToStdErr("Notice");
    } else {
        if (m_bSyslog) {
            vsyslog(LOG_NOTICE, msg, ap);
        } else {
            vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
            saveToFile("Notice");
        }
    }
    va_end(ap);
}

void Logger::addInfo(const char *msg, ...)
{
    if (m_iTrimLevel < LOG_INFO) return;

    va_list(ap);
    va_start(ap, msg);

    if (m_bToConsole) {
        vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
        saveToStdErr("Info");
    } else {
        if (m_bSyslog) {
            vsyslog(LOG_INFO, msg, ap);
        } else {
            vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
            saveToFile("Info");
        }
    }
    va_end(ap);
}

void Logger::addDebug(const char *msg, ...)
{
    if (m_iTrimLevel < LOG_DEBUG) return;

    va_list(ap);
    va_start(ap, msg);

    if (m_bToConsole) {
        vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
        saveToStdErr("Debug");
    } else {
        if (m_bSyslog) {
            vsyslog(LOG_DEBUG, msg, ap);
        } else {
            vsnprintf(m_pchBuf, m_iBufSize, msg, ap);
            saveToFile("Debug");
        }
    }
    va_end(ap);
}

void Logger::saveToFile(const char *pchSuffix)
{
        int fd = open(m_pchLogName, O_CREAT | O_APPEND | O_WRONLY,
			S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);

	if (fd<0) {
                printf("Can't open file %s\n",m_pchLogName);
		perror("addError: ");
		return;
	}

	//формируем строку вывода
	struct timeval tv;
	gettimeofday(&tv,NULL);

	struct tm* ptm;
	ptm = localtime(&tv.tv_sec);

        char chtime[40];
        snprintf(chtime, 40, "%d:%d:%d.%03ld.%03ld %s: ",ptm->tm_hour, ptm->tm_min, ptm->tm_sec,
                        tv.tv_usec/1000, tv.tv_usec%1000, pchSuffix);

        int len = strlen(m_pchBuf)+41;
	char *str = new char[len];
	strcpy(str,chtime);
        strcat(str,m_pchBuf);
	strcat(str,"\n");

	len = strlen(str)+1;
	int index = 0;
	while(1) {
 		int res = write (fd,str+index,len);
		if (res<0) {
			// Ошибка записи!
			std::cerr << "[ZLogger::saveToFile()] Can't write to file: " << strerror(errno) << std::endl;
			break;
		}
		if (res<len) {
			index += res;
			len -= res;
		} else break;
	}

	fsync(fd);
	close(fd);

	delete [] str;
}

void Logger::saveToStdErr(const char *pchSuffix)
{
        //формируем строку вывода
        struct timeval tv;
        gettimeofday(&tv,NULL);

        struct tm* ptm;
        ptm = localtime(&tv.tv_sec);

        char chtime[40];
        snprintf(chtime, 40, "%d:%d:%d.%03ld.%03ld %s: ",ptm->tm_hour, ptm->tm_min, ptm->tm_sec,
                        tv.tv_usec/1000, tv.tv_usec%1000, pchSuffix);

        std::cerr << chtime << m_pchBuf << std::endl;
}

